package de.rockon.fuzzy.controller.gui.tools;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.DefaultTableModel;

import de.rockon.fuzzy.controller.gui.MainFrame;
import de.rockon.fuzzy.controller.model.FuzzyController;
import de.rockon.fuzzy.controller.model.FuzzyVariable;
import de.rockon.fuzzy.controller.model.commands.ActionCommandPool;
import de.rockon.fuzzy.controller.model.enums.VariableType;
import de.rockon.fuzzy.controller.util.Logger;
import de.rockon.fuzzy.controller.util.factories.IconFactory;
import de.rockon.fuzzy.controller.util.factories.UIFactory;
import de.rockon.fuzzy.io.ImportExportUtil;

public class DatasetImporter extends JFrame implements ActionListener {
	private static final long serialVersionUID = -5942122019359673219L;

	private ArrayList<FuzzyVariable> variables = new ArrayList<FuzzyVariable>();
	
	private ArrayList<FuzzyVariable> outputVariables = new ArrayList<FuzzyVariable>();

	private FuzzyController controller = FuzzyController.getInstance();
	
	private ArrayList<FuzzyTestCase> testCases = new ArrayList<FuzzyTestCase>();
	
	private JTable dataTable = new JTable();
		
	private MyTableModel datamodel;
	
	private int numOutputs = 0;

	private JButton importButton;

	private JButton testButton;

	private JButton exportButton;

	private JButton closeButton;	
	
	/** Reference to MainFrame */
	private MainFrame mainFrame;
	
	public DatasetImporter(MainFrame mainFrame) {
		if (controller.getContent().size() == 0) {
			UIFactory.showErrorDialog(this, "Empty Fuzzy Controller!");
		} 
		
		this.mainFrame = mainFrame;
		datamodel = new MyTableModel();
		dataTable.getTableHeader().setReorderingAllowed(false);
		dataTable.setModel(datamodel);
		
		for (int i= 0; i<controller.sizeOutput(); i++) {
			outputVariables.add(controller.getVariableByType(i, VariableType.OUTPUT));
		}
		
		initGUI();
	}
	
	private JPanel getControlPanel() {
		JPanel content = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		
		testButton = UIFactory.getJButton("Run Tests", ActionCommandPool.RUN_TEST, IconFactory.ICON_TEST, this);
		importButton = UIFactory.getJButton("Import", ActionCommandPool.IMPORT_DATA, IconFactory.ICON_IMPORT, this);
		exportButton = UIFactory.getJButton("Export", ActionCommandPool.EXPORT_DATA, IconFactory.ICON_EXPORT, this);
		closeButton = UIFactory.getJButton("Close", ActionCommandPool.EXIT, IconFactory.ICON_EXIT, this);
		
		exportButton.setEnabled(false);
		testButton.setEnabled(false);
		
		content.add(testButton);
		content.add(importButton);
		content.add(exportButton);
		content.add(closeButton);
		
		return content;
	}
	
	private void initGUI() {
		this.setTitle("Data Importer");
		setIconImage(IconFactory.getInstance().getImageIcon(IconFactory.ICON_TEST).getImage());

		this.setLayout(new BorderLayout());
	
		JScrollPane pane = new JScrollPane(dataTable);
		pane.setBorder(new TitledBorder(new EtchedBorder(), "Test Data"));
		
		this.add(pane, BorderLayout.CENTER);
		this.add(getControlPanel(), BorderLayout.SOUTH);
		
//		this.pack();
		this.setSize(800,600);
		this.setVisible(true);
	}

	private boolean loadFile() {
		String fileName = ImportExportUtil.chooseFileToLoad(this, new FileNameExtensionFilter("CSV Files", "csv", "csv"));
		if (fileName != null) {
			File importFile = new File(fileName);
			testCases.clear();

			try {
				BufferedReader in = new BufferedReader(new FileReader(importFile));
				String line = null;
				int lineCounter = 0;
				while ((line = in.readLine()) != null) {
					String[] content = line.split(";");

					if (content.length != controller.sizeInput() + 1) {
						UIFactory.showErrorDialog(this, "Data file not valid. Too much or missing values detected.");
						return false;
					}
					
					/* Fuzzy-Variablen */
					if (lineCounter == 0) {
						for (int i=1; i<content.length; i++) {
							if (controller.getChildByName(content[i]) == null) {
								Logger.error("Variable " + content[i] + " not found in Controller.");
								UIFactory.showErrorDialog(this, "Imported data seems not to match the controller file.");
								return false;
							}
							variables.add(controller.getChildByName(content[i]));
						}
						
					/* Werte */
					} else {
						FuzzyTestCase testCase = new FuzzyTestCase(content[0]);
						
						for (int i=1; i<content.length; i++) {
							testCase.addTestValueForVariable(variables.get(i-1), Double.parseDouble(content[i]));
						}
						testCases.add(testCase);
					}					
					lineCounter++;
				}
				
			} catch (IOException e) {
				e.printStackTrace();
			}
			this.numOutputs = controller.sizeOutput();
			updateTableModel();		
			
			testButton.setEnabled(true);
			importButton.setEnabled(false);
			
			return true;
		}
		return false;
	}
	
	private void saveFile() {
		String fileName = ImportExportUtil.chooseFileToSave(this, new FileNameExtensionFilter("CSV Files", "csv", "csv"));
		if (fileName != null) {
			File exportFile = new File(fileName);
			
			try {
				FileWriter fstream = new FileWriter(exportFile);
				BufferedWriter out = new BufferedWriter(fstream);
				
				out.write("Identifier");
				for (FuzzyVariable var : outputVariables) {
					out.write(";" + var.getName());
				}
				out.write("\n");
				
				for (FuzzyTestCase testCase: testCases) {
					out.write(testCase.getIdentifier());
					for (FuzzyVariable var : outputVariables) {
						out.write(";" + testCase.getResultValueForVariable(var));
					}
					out.write("\n");
				}
				out.close();
			} catch (Exception e) {
				Logger.error("Error: " + e.getMessage());
			}	
		}		
	}
	
	private void updateTableModel() {
		datamodel.fireTableStructureChanged();
		datamodel.fireTableDataChanged();
		SwingUtilities.updateComponentTreeUI(this);
		dataTable.updateUI();
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		final String cmd = e.getActionCommand();

		if (cmd.equals(ActionCommandPool.IMPORT_DATA)) {
			if (loadFile()) {
				UIFactory.showInfoDialog(this, testCases.size() + " test cases loaded.");
			}
		} else if (cmd.equals(ActionCommandPool.EXPORT_DATA)) {
			saveFile();
			UIFactory.showInfoDialog(this, testCases.size() + " test results exported.");
		} else if (cmd.equals(ActionCommandPool.RUN_TEST)) {
			
			mainFrame.setUiUpdate(false);
			for (FuzzyTestCase testCase: testCases) {
				testCase.runTest();				
				updateTableModel();				
			}
			mainFrame.setUiUpdate(true);
			
			exportButton.setEnabled(true);
		} else {
			this.dispose();
		}
	}

	class MyTableModel extends DefaultTableModel {

		@Override
		public int getColumnCount() {
			return variables.size() + 1 + numOutputs;
		}

		@Override
		public String getColumnName(int column) {
			if (variables.isEmpty()) return "";
			
			if (column == 0) {
				return "Identifier";
			} else if (column >= 1 && column <= controller.sizeInput()) {
				return variables.get(column - 1).getName() + " [" + variables.get(column - 1).getUnit() + "]";
			} else {
				return outputVariables.get(column - 1 - controller.sizeInput()).getName() + " [" + 
				outputVariables.get(column - 1 - controller.sizeInput()).getUnit() + "]";
			}
		}

		@Override
		public int getRowCount() {
			return testCases.size();
		}

		@Override
		public Object getValueAt(int row, int column) {
			if (variables.isEmpty()) return "";
			
			if (column == 0) {
				return testCases.get(row).getIdentifier();
			} else if (column >= 1 && column <= controller.sizeInput()  ) {
				return testCases.get(row).getValueForVariable(variables.get(column-1));
			} else {
				return testCases.get(row).getResultValueForVariable(outputVariables.get(column - 1 - controller.sizeInput()));
			}
		}
	}
}